package com.atom.compiler.ksp.aap

import com.atom.compiler.ksp.core.KspLog
import com.atom.module.annotation.aap.AapAutoClass
import com.atom.module.annotation.aap.AapImplEntry
import com.squareup.kotlinpoet.*
import com.squareup.kotlinpoet.ksp.toClassName
import com.squareup.kotlinpoet.ksp.writeTo
import java.io.IOException
import java.util.*

class AapMetas(private val aapContext: AapContext) {

    private val aapPacketName = AapOptions.AAP_PACKET
    private val aapModuleName: String = aapContext.moduleName
    private val classBuilder: TypeSpec.Builder =
        TypeSpec.classBuilder(ClassName(aapPacketName, aapModuleName)).apply {
            modifiers.add(KModifier.PUBLIC)
        }

    fun addMetasCode(apis: Set<AapMeta>): AapMetas {
        // add builder super class
        classBuilder.superclass(ClassName.bestGuess(AapImplEntry::class.qualifiedName!!))
        val formatDateValue: String = aapContext.dateFormat.format(Date())
        // 代码创建 文档
        classBuilder.addKdoc(
            CodeBlock.builder()
                .add("<p>This is a class automatically generated by API annotation processor.</p>")
                .add("\n")
                .add("@date ").add(formatDateValue)
                .add("\n")
                .build()
        )
        classBuilder.addAnnotation(
            AnnotationSpec.builder(AapAutoClass::class.java)
                .addMember("value = [%S]", AapSymbolProcessor::class.java.simpleName)
                .addMember("data = %S", formatDateValue)
                .build()
        )
        classBuilder.addAnnotation(
            AnnotationSpec.builder(SuppressWarnings::class.java)
                .addMember("value = [%S]", "all")
                .build()
        )
        classBuilder.addFunction(FunSpec.constructorBuilder().apply {
            this.modifiers.add(KModifier.PUBLIC)
            this.callSuperConstructor()
            for (api in apis) {
                if (aapContext.classSet.contains(api.apiTypeElement)) {
                    continue
                } else {
                    aapContext.classSet.add(api.apiTypeElement)
                }
                val singleList: MutableList<SingleImpl> = ArrayList()
                getImplNames(api, apis, singleList)
                for (impl in singleList) {
                    println("  >>>>>>  $impl")
                    this.addStatement(
                        "add( %S , %T::class.java , %T::class.java , ${impl.version})",
                        impl.name, impl.apiClass, impl.implClass,
                    )
                }
            }
        }.build())
        return this;
    }

    fun assembleCode() {
        FileSpec.get(aapPacketName, classBuilder.build())
            .writeTo(aapContext.context.environment.codeGenerator, aggregating = true)
    }

    private fun getImplNames(
        api: AapMeta,
        impls: Collection<AapMeta>,
        result: MutableList<SingleImpl>
    ) {
        val apiClassName = api.apiTypeElement.toClassName()
        for (metaApi in impls) {
            if (metaApi.apiTypeElement != api.apiTypeElement) {
                continue
            }
            result.add(
                SingleImpl(
                    apiClassName,
                    metaApi.implTypeElement.toClassName(),
                    metaApi.implName,
                    metaApi.implVersion
                )
            )
        }
    }

    internal data class SingleImpl(
        val apiClass: ClassName,
        val implClass: ClassName,
        val name: String,
        val version: Long
    )
}